/************************************************************************
                                                                     
   Xceed Ultimate ListBox for Silverlight                                                                                                                                            
   Copyright (C) 2010 Xceed Software Inc.    
                                                                     
   This program is provided to you under the terms of the GNU General Public  
   License version 2 as published by the Free Software Foundation. 
        
   This program is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY, without even the implied warranty of 
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License along 
   with this program, if not, write to the Free Software Foundation, Inc., 
   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

   This program can be provided to you by Xceed Software Inc. under a
   proprietary commercial license agreement for use in non-Open Source
   projects. Visit Xceed.com to find the commercial edition of 
   Xceed Ultimate ListBox for Silverlight.                                    
                                                                      
 **********************************************************************/

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Globalization;

namespace Xceed.Silverlight.Data
{
  internal static class AsyncResultsExtensions
  {
    #region Async execution extensions
    internal static AsyncResult CastAsyncResult( this IAsyncResult asyncResult )
    {
      AsyncResult asyncResultImpl = asyncResult as AsyncResult;

      if( asyncResultImpl == null )
        ThrowAsyncValidationError( "The IAsyncResult is not an AsyncResult instance." );

      return asyncResultImpl;
    }

    internal static AsyncResultBase CastAsyncResultBase( this IAsyncResult asyncResult )
    {
      AsyncResultBase asyncResultImpl = asyncResult as AsyncResultBase;

      if( asyncResultImpl == null )
        ThrowAsyncValidationError( "The IAsyncResult is not an AsyncResultBase instance." );

      return asyncResultImpl;
    }

    internal static ContextAsyncResult CastContextAsyncResult( this IAsyncResult asyncResult )
    {
      ContextAsyncResult asyncResultImpl = asyncResult as ContextAsyncResult;

      if( asyncResultImpl == null )
        ThrowAsyncValidationError( "The IAsyncResult is not a ContextAsyncResult instance." );

      return asyncResultImpl;
    }

    public static T CastIAsyncResult<T>( this IAsyncResult asyncResult ) where T : IAsyncResult
    {
      if( !typeof( T ).IsAssignableFrom( asyncResult.GetType() ) )
        ThrowAsyncValidationError( string.Format( "The IAsyncResult is not a {0} instance.", typeof( T ).Name ) );

      return ( T )asyncResult;
    }

    public static void ValidateCallEnd( this AsyncResultBase asyncResult, object callId )
    {
      if( !asyncResult.UseCallId( callId ) )
        ThrowAsyncValidationError( "The reference does not belong to the expected call." );

      if( !asyncResult.IsCompleted )
        ThrowAsyncValidationError( "The operation is not complete." );
    }

    public static void ValidateCallEnd( this IAsyncResult asyncResult, object callId )
    {
      AsyncResultBase asyncResultImpl = asyncResult.CastAsyncResultBase();
      asyncResultImpl.ValidateCallEnd( callId );
    }

    public static bool UseCallId( this IAsyncResult asyncResult, object callId )
    {
      AsyncResultBase asyncResultImpl = asyncResult as AsyncResultBase;

      return ( asyncResultImpl != null )
        && asyncResultImpl.UseCallId( callId );
    }

    public static bool UseCallId( this AsyncResultBase asyncResult, object callId )
    {
      bool isSameCallId = ( asyncResult.CallId == null && callId == null )
              || ( asyncResult.CallId != null && asyncResult.CallId.Equals( callId ) );

      return isSameCallId;
    }

    public static T GetLastAsyncResult<T>( this IAsyncResult parentAsyncResult ) where T : IAsyncResult
    {
      AsyncResult asyncResult = parentAsyncResult.CastAsyncResult();
      string wrongTypeName = null;

      if( asyncResult.LastIAsyncResult == null )
      {
        wrongTypeName = "null";
      }
      else if( !typeof( T ).IsAssignableFrom( asyncResult.LastIAsyncResult.GetType() ) )
      {
        wrongTypeName = asyncResult.LastIAsyncResult.GetType().Name;
      }

      if( wrongTypeName != null )
        ThrowAsyncValidationError( string.Format( CultureInfo.CurrentCulture, "The child AsyncResult is not of the expected type. Expected={0}, Actual={1} )", typeof( T ), wrongTypeName ) );

      return ( T )asyncResult.LastIAsyncResult;
    }

    public static T GetCallContext<T>( this IAsyncResult parentAsyncResult )
    {
      ContextAsyncResult asyncResult = parentAsyncResult.CastContextAsyncResult();
      string wrongTypeName = null;

      if( !typeof( T ).IsAssignableFrom( asyncResult.CallContext.GetType() ) )
      {
        wrongTypeName = asyncResult.CallContext.GetType().Name;
      }

      if( wrongTypeName != null )
        ThrowAsyncValidationError( string.Format( CultureInfo.CurrentCulture, "The CallContext is not of the expected type. Expected={0}, Actual={1} )", typeof( T ), wrongTypeName ) );

      return ( T )asyncResult.CallContext;
    }



    #endregion Async execution extensions

    #region AsyncResult Util Properties

    public static int GetMoveUnit( this MoveAndFetchAsyncResult asyncResult )
    {
      return DataUtils.GetUnit( asyncResult.ExpectedMoveCount );
    }

    public static int GetFetchUnit( this MoveAndFetchAsyncResult asyncResult )
    {
      return DataUtils.GetUnit( asyncResult.ExpectedFetchCount );
    }

    public static bool IsMovingForward( this MoveAndFetchAsyncResult asyncResult )
    {
      if( asyncResult.ExpectedMoveCount == 0 )
        throw new InvalidOperationException( "No moves were made with this operation." );

      return ( asyncResult.ExpectedMoveCount > 0 );
    }

    public static bool IsFetchingForward( this MoveAndFetchAsyncResult asyncResult )
    {
      if(asyncResult.ExpectedFetchCount == 0)
        throw new InvalidOperationException( "No fetches were made with this operation." );

      return ( asyncResult.ExpectedFetchCount > 0 );
    }

    public static JumpMoveAndFetchParameters GetParameters(this JumpMoveAndFetchAsyncResult asyncResult )
    {
      return new JumpMoveAndFetchParameters( 
        asyncResult.SourcePosition, 
        asyncResult.ExpectedMoveCount, 
        asyncResult.ExpectedFetchCount );
    }



    #endregion AsyncResult Util Properties

    #region Sync execution extensions

    internal static AsyncResult ExecuteSync( Guid callId, AsyncCallback userCallback, object asyncState, DataEnumeratorResult result )
    {
      AsyncResult asyncResult = new AsyncResult( callId, userCallback, asyncState );
      asyncResult.StartSync( result );
      return asyncResult;
    }

    public static T GetCallResult<T>( this IAsyncResult asyncResult )
    {
      AsyncResult asyncResultImpl = asyncResult.CastAsyncResult();

      if( asyncResultImpl.LastResultData != null )
      {
        if( !typeof( T ).IsAssignableFrom( asyncResultImpl.LastResultData.GetType() ) )
          ThrowAsyncValidationError( "The call result is not of the expected type." );
      }

      return ( T )asyncResultImpl.LastResultData;
    }


    public static T EnsureSyncness<T>( this T asyncResult ) where T : IAsyncResult
    {
      if( asyncResult == null )
        throw new ArgumentNullException( "asyncResult" );

      if( !asyncResult.IsCompleted || !asyncResult.CompletedSynchronously )
        throw new InvalidOperationException( "The child must always execute synchronously." );

      return asyncResult;
    }

    #endregion Sync execution extensions

    private static void ThrowAsyncValidationError( string details )
    {
      if( details == null )
        details = string.Empty;

      throw new ArgumentException( string.Format( CultureInfo.CurrentCulture, "The specified IAsyncResult reference is invalid. {0}", details ) );
    }
  }
}
